

(function(factory) {

  // Establish the root object, `window` (`self`) in the browser, or `global` on the server.
  // We use `self` instead of `window` for `WebWorker` support.
  var root = (typeof self == 'object' && self.self == self && self) ||
            (typeof global == 'object' && global.global == global && global);

  // Set up CV appropriately for the environment. Start with AMD.
  if (typeof define === 'function' && define.amd) {
    define(['underscore', 'jquery', 'exports'], function(_, $, exports) {
      // Export global even in AMD case in case this script is loaded with
      // others that may still expect a global CV.
      root.CV = factory(root, exports, _, $);
    });

  // Next for Node.js or CommonJS. jQuery may not be needed as a module.
  } else if (typeof exports !== 'undefined') {
    var _ = require('underscore'), $;
    try { $ = require('jquery'); } catch(e) {}
    factory(root, exports, _, $);

  // Finally, as a browser global.
  } else {
    root.CV = factory(root, {}, root._, (root.jQuery || root.Zepto || root.ender || root.$));
  }


}(function(root, CV, _, $) {

  var ControllerView = CV.ControllerView = function (options) {
    _.extend(this, _.pick(options, viewOptions));
    this._ensureElement();
    this.initBinding();
    this.initialize.apply(this, arguments);
  }

  // Cached regex to split keys for `delegate`.
  var delegateEventSplitter = /^(\S+)\s*(.*)$/;
  // List of view options to be set as properties.
  var viewOptions = [ 'el', 'id', 'attributes', 'className', 'tagName', 'events', 'initKey'];

  _.extend(ControllerView.prototype, {
    _ensureElement: function () {
      this.setElement();
    },
    setElement: function () {
      this._setElement(this.el);
      this.delegateEvents();
      return this;
    },
    _setElement: function(el) {
      this.$el = el instanceof $ ? el : $(el);
      this.el = this.$el[0];
    },
    delegateEvents: function(events) {
      events || (events = _.result(this, 'events'));
      if (!events) return this;
      this.undelegateEvents();
      for (var key in events) {
        var method = events[key];
        if (!_.isFunction(method)) method = this[method];
        if (!method) continue;
        var match = key.match(delegateEventSplitter);
        this.delegate(match[1], match[2], _.bind(method, this));
      }
      return this;
    },
    undelegateEvents: function() {
      // if (this.$el) this.$el.off('.delegateEvents' + this.cid);
      return this;
    },
    // Add a single event listener to the view's element (or a child element
    // using `selector`). This only works for delegate-able events: not `focus`,
    // `blur`, and not `change`, `submit`, and `reset` in Internet Explorer.
    delegate: function(eventName, selector, listener) {
      this.$el.on(eventName, selector, listener);
      return this;
    },
    initialize: function () {

    }
  })
  // Helper function to correctly set up the prototype chain for subclasses.
  // Similar to `goog.inherits`, but uses a hash of prototype properties and
  // class properties to be extended.
  var extend = function(protoProps, staticProps) {
    var parent = this;
    var child;

    // The constructor function for the new subclass is either defined by you
    // (the "constructor" property in your `extend` definition), or defaulted
    // by us to simply call the parent constructor.
    if (protoProps && _.has(protoProps, 'constructor')) {
      child = protoProps.constructor;
    } else {
      child = function(){ return parent.apply(this, arguments); };
    }

    // Add static properties to the constructor function, if supplied.
    _.extend(child, parent, staticProps);

    // Set the prototype chain to inherit from `parent`, without calling
    // `parent` constructor function.
    var Surrogate = function(){ this.constructor = child; };
    Surrogate.prototype = parent.prototype;
    child.prototype = new Surrogate;

    // Add prototype properties (instance properties) to the subclass,
    // if supplied.
    if (protoProps) _.extend(child.prototype, protoProps);

    // Set a convenience property in case the parent's prototype is needed
    // later.
    child.__super__ = parent.prototype;

    return child;
  };

  // Set up inheritance for the model, collection, router, view and history.
  ControllerView.extend = extend;



  //http sample
   var httpService = CV.httpService = function(type, url, params, suc, fail) {
    $.ajax({
      type: type,
      url: url,
      data: params,
      success: function(data) {
        (suc && typeof(suc) === 'function') && suc(data);
      },
      error: function(data) {
        (fail && typeof(fail) === 'function') && suc(data);
      },
      timeout: 15000
    });
  };

   //属性监听
   var Monitor = {
     bindingMark: 'data-element-binding',
     bindings: {},
     observer:{},
     initBinding: function () {
      var self = this;
      if (!this.el) return;
      this.el.innerHTML = this.el.innerHTML.replace(/\[\{(.*)\}\]/g, this.markToken.bind(this));
      this.previousHTML = this.el.innerHTML;
      this.el.innerHTML = this.el.innerHTML.replace(/cv-model=\"(.*)\"/g, this.markTokenModel.bind(this));

      for (var variable in this.bindings) {
          this.bind(variable); //将每个数据的名称比如'msg'绑定到data
      }
      // this.el.innerHTML = this.previousHTML;
      var initKey = this.initKey;
      if (initKey) {
          for (var variable in initKey) {
              this.observer[variable] = initKey[variable]
          }
      }
     },
     bind: function(variable) {
       var bindings = this.bindings, bindingMark = this.bindingMark,el = this.el, observer = this.observer;
       bindings[variable].els = el.querySelectorAll('[' + bindingMark + '="' + variable + '"]'); //bindings里再存储msg绑定的元素
      //  [].forEach.call(bindings[variable].els, function(e) { //删除data-element-binding属性
      //    e.removeAttribute(bindingMark)
      //  })
       Object.defineProperty(observer, variable, { //es5观察属性
         set: function(newVal) {
           [].forEach.call(bindings[variable].els, function(e) {
             bindings[variable].value = e.textContent = e.value = newVal //=>这里才是实现的绑定,更新数据到dom并更新内部暂存数据
           })
         },
         get: function() {
           return bindings[variable].value //取数据仅仅是内部暂存的数据
         }
       })

     },
     markToken: function (match, variable) {
       this.bindings[variable] = {} //bindings里存储了数据来源的字段比如bindings['msg']
       return '<span ' + this.bindingMark + '="' + variable +'"></span>'
     },
     markTokenModel: function (match, variable) {
       this.bindings[variable] = {};
       return  this.bindingMark + '="' + variable +'"' //内填一个span变为只改它的元素
     }

   }
   _.extend(ControllerView.prototype, Monitor);


  return CV;
}))
